package com.gitee.feizns.quickstart.spring;

import com.gitee.feizns.dynamic.Ex;
import lombok.Getter;
import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.lang.NonNull;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * Spring容器工具类
 * @author feizns
 * @since 2023/2/3
 */
public class AppUtils implements
        ApplicationContextAware,
        BeanFactoryAware,
        ApplicationEventPublisherAware,
        ResourceLoaderAware {

    /**
     * @see ApplicationContext
     */
    @Getter
    private static ApplicationContext applicationContext;

    /**
     * bean工厂
     * @see BeanFactory
     */
    @Getter
    private static BeanFactory beanFactory;

    /**
     * 资源加载器
     */
    @Getter
    private static ResourceLoader resourceLoader;

    /**
     * 应用程序事件发布者
     * @see ApplicationEventPublisher
     */
    @Getter
    private static ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
        AppUtils.beanFactory = beanFactory;
    }

    /**
     * 静态设置bean工厂
     * @param beanFactory {@link BeanFactory}
     */
    public static void staticSetBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
        AppUtils.beanFactory = beanFactory;
    }

    /**
     * static set 应用程序上下文
     * @param applicationContext 应用程序上下文
     * @throws BeansException bean 异常
     */
    public static void staticSetApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        AppUtils.applicationContext = applicationContext;
    }

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        AppUtils.applicationContext = applicationContext;
    }

    @Override
    public void setApplicationEventPublisher(@NonNull ApplicationEventPublisher applicationEventPublisher) {
        AppUtils.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     * 获取对应名称的bean
     * @param name bean名称
     * @return {@link Object}
     */
    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    /**
     * 从Spring容器中获取对应类型的实例
     * @param type {@link Class}
     * @return 容器类型实例
     * @param <T> 类型
     */
    public static <T> T getBean(Class<T> type) {
        return applicationContext.getBean(type);
    }

    /**
     * 获取对应名称的bean实例
     * @param name 名称
     * @param type {@link Class}
     * @return 类型
     * @param <T> 类型
     */
    public static <T> T getBean(String name, Class<T> type) {
        return applicationContext.getBean(name, type);
    }

    /**
     * 获取类型对应类型的bean实例列表
     * @param type bean类型
     * @return {@link Collection}
     * @param <T> bean类型
     */
    public static <T> Collection<T> getBeans(Class<T> type) {
        return getBeansOfType(type).values();
    }

    /**
     * 获取类型对应类型的bean实例
     * @param type bean类型
     * @return {@link Map} key=bean名称，value=bean实例
     * @param <T> bean类型
     */
    public static <T> Map<String, T> getBeansOfType(Class<T> type) {
        return applicationContext.getBeansOfType(type);
    }

    /**
     * 查询对应注解的所有bean
     * @param annotationType {@link Annotation}
     * @return bean名称列表
     * @param <T> 注解类型
     */
    public static <T extends Annotation> String[] getBeanNamesForAnnotation(Class<T> annotationType) {
        return applicationContext.getBeanNamesForAnnotation(annotationType);
    }

    /**
     * 获取对应注解的所有bean
     * @param annotationType {@link Annotation} 注解类型
     * @return {@link Map} key=bean名称，value=bean实例
     * @param <T> 注解类型
     */
    public static <T extends Annotation> Map<String, Object> getBeanForAnnotation(Class<T> annotationType) {
        return Arrays.stream(getBeanNamesForAnnotation(annotationType))
                .collect(Collectors.toMap(Function.identity(), AppUtils::getBean));
    }

    /**
     * 注册bean
     * @param bean bean实例
     * @param <T> bean类型
     */
    public static <T> void registry(T bean) {
        registry(bean.getClass().getName(), bean);
    }

    /**
     * 注册bean
     * @param beanName bean名称
     * @param bean bean实例
     * @param <T> bean类型
     */
    public static <T> void registry(String beanName, T bean) {
        @SuppressWarnings("unchecked")
        Class<T> beanClass = (Class<T>) bean.getClass();
        registry(beanClass, beanName, () -> bean);
    }

    /**
     * 注册bean
     * @param beanType bean类型
     * @param beanName bean名称
     * @param supplier bean提供者
     * @param <T> bean类型
     */
    public static <T> void registry(Class<T> beanType, String beanName, Supplier<T> supplier) {
        //获取BeanFactory
        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
        //创建bean信息.
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(beanType, supplier);
        //注册bean
        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
    }

    @Override
    public void setResourceLoader(@NonNull ResourceLoader resourceLoader) {
        AppUtils.resourceLoader = resourceLoader;
    }

    /**
     * 获取类下所有文件
     * @return {@link List}<{@link Resource}>
     */
    @SneakyThrows
    public static List<Resource> resources(String packageName) {
        ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        Resource[] resources = resolver.getResources("classpath*:" + packageName.replace(".", "/") + "/**/*.class");
        return Arrays.stream(resources).collect(Collectors.toList());
    }


    /**
     * 获取类下所有文件
     * @param packageName 包名称
     * @return {@link List}<{@link MetadataReader}>
     */
    @SneakyThrows
    public static List<MetadataReader> metadataReaders(String packageName) {
        MetadataReaderFactory metaReader = new CachingMetadataReaderFactory(resourceLoader);
        return resources(packageName).stream()
                .map(resource -> Ex.tryCatch(() -> metaReader.getMetadataReader(resource)).orElseThrow())
                .collect(Collectors.toList());
    }

}
